require 'widget_builder/constants'
require 'awesome_print'
require 'yui/compressor'
require 'html_uglifier'
require 'premailer'
require 'sass'
require 'find'

module WidgetBuilder

	# Creates a single Widget by passing in the path to the html, css, and js files.
	class Widget
		def initialize(html_path, css_path, js_path, options = {})
			@html_path, @css_path, @js_path = html_path, css_path, js_path

			defaults = { compress: true }
			defaults.merge(options).each do |k, v|
				instance_variable_set("@#{k}",v)
			end

			@html = html
			@widget_name ||= widget_name # the widget name is either passed in as an option or extrapolated from the html
			@href = href

			@css = css
			@js = js
		end

		def html
			File.read @html_path
		end

		# Get the widget name from the class of the first div tag in the html
		def widget_name
			@html.match(/<div\s+?class=(['"])(\w+)(?:\s+\w*)*\1/i)[2]
		end

		def css
			css = File.read @css_path # Reads the css or scss file
			css_folder = File.dirname @css_path # Infers the css foler

			# Scans the file for any @import rules and includes their content
			css.scan(/(^\s*@import\s+(?:url[\( ])?(['"])([\w\.]+)\2.*;)/).each do |m|
				Find.find(css_folder) do |path|
					css.gsub!(m[0], File.read(path)) if path.match(m[2])
				end
			end

			# Determines the suffics of the file
			suffix = @css_path.split('.').last
			if suffix == 'css' # Just return the css if it is a plain css file
				css
			elsif suffix =~ /^scss|sass$/ # handle Sass files with the Sass Engine
				engine = Sass::Engine.new(css, syntax: suffix.to_sym)
				engine.render # converts scss or sass to plain css
			else
				raise 'Invalid css format'
			end
		end

		def html_with_css
			premailer = Premailer.new(@html, { with_html_string: true, remove_comments: true, css_string: @css })
			html_with_css = premailer.to_inline_css # places all the css inline with the html code
			html_with_css = WidgetBuilder.remove_excess_premailer_content(html_with_css) # cleans up excess styles
			if @compress == true
				HtmlUglifier::Compressor.new.compress(html_with_css).gsub(/\n/, '')
			else
				html_with_css.gsub("\n", " \\\n") # returns the html as is but adds trailing backslashes after each line to allow the multiline string in JavaScript
			end
		end

		# Properly formats the href for the widget a tag or returns '#' if not provided
		def href
			if href = @href
				href.gsub!(/^http:\/\//, '') # removes leading http:// if present since it gets added in the next step
				"http://#{href}"
			else
				'#'
			end
		end

		# Generates the final js file with all included html & css
		def js
			js = File.read WidgetBuilder::WIDGET_LOADER_PATH # reads the widget_loader js
			js.gsub!(/widget_name/, @widget_name) # adds the widget name to the js file
			js.gsub!(/JAVASCRIPT_PLACEHOLDER/, File.read(@js_path)) # adds the custom js to the widget_loader js
			js.gsub!(/HTML&CSS_PLACEHOLDER/, html_with_css) # includes the html & css
			js.gsub!(/HREF_PLACEHOLDER/, @href) # adds the href to the js validateReference function
			js.gsub!(/VALID_ATAG_PLACEHOLDER/, %Q{<a class="#{@widget_name}" href="#{@href}"></a>})
			if @compress == true 
				YUI::JavaScriptCompressor.new(munge: true).compress(js) # uglifies the combined javascript
			else
				js # Otherwise returns the raw non-minified javascript
			end
		end

		# Saves the final js file to the specified path
		def save(path = File.dirname(@js_path))
			new_js_path = File.join(path, @widget_name + '.js')
			File.open(new_js_path, 'w') do |f|
				f.write @js
			end
		end

		def show_snippet
			# Properly format or generate the javascript widget src path
			if source_path = @source_path
				source_path.gsub!(/^http:\/\//, '') # removes leading http:// if present since it gets added later
				source_path.gsub!(/\/$/, '') # removes trailing slash if present since it gets added later
			else
				source_path = 'SOURCE_PATH_PLACEHOLDER'
				ap "NOTE: Make sure to replace the #{source_path} in the code snippet below.", color: {string: :redish}	
			end

			ap "------------ Copy the following HTML snippet to display the widget. -------------", color: {string: :cyanish}
puts %Q{<a class="#{@widget_name}" href="#{@href}"></a>
<script type="text/javascript">
   var element = document.createElement('script');
   element.type = 'text/javascript';
   element.src = 'http://#{source_path}/#{@widget_name}.js';
   document.getElementsByTagName('head')[0].appendChild(element);
</script>}
			ap "---------------------------------------------------------------------------------", color: {string: :cyanish}
		end

		# Outputs a nice report on the console
		def report
			puts 
			ap "Widget name: #{@widget_name}",								color: {string: :redish}
			ap "HTML Template: #{@html_path.split('/').last}",			color: {string: :greenish}
			ap "Javascript reference: #{@js_path.split('/').last}",	color: {string: :greenish}
			ap "Stylesheet reference: #{@css_path.split('/').last}", color: {string: :greenish}
			ap "HTML with CSS:"
			puts html_with_css
			ap "Final Compilation of the JS File (includes html, css, and js):", color: {string: :greenish}
			puts @js
			show_snippet
		end

		def compile(path = File.dirname(@js_path))
			save(path)
			report
		end
	end

	# Crates an array of Widgets by passing in the paths to the html, css, and js directories
	class Widgets
		def initialize(html_path = '.', css_path = '.', js_path = '.', options = {})
			@html_path, @css_path, @js_path, @options = html_path, css_path, js_path, options
			@widgets = widgets
		end

		def widgets
			widgets = []
			Dir.foreach(@html_path) do |file| # read all the files in the 'Widget html' directory
				if (file.match(/\.html$/)) # only consider html files for now
					html_path = File.join(@html_path, file)

					# HTML, CSS, and JS files should have the same name but different extensions
					filename = file.gsub('.html', '')

					css_path = ''
					Find.find(@css_path) do |path|
						css_path = path if path.split('/').last.match(/^#{filename}.*?\.(?:css|scss|sass)$/)
					end

					js_path = ''
					Find.find(@js_path) do |path|
						js_path = path if path.split('/').last.match(/^#{filename}\.js$/) # NOTE: Needs to add CoffeeScript support
					end
					widgets << WidgetBuilder::Widget.new(html_path, css_path, js_path, @options)
				end
			end
			widgets
		end

		def save(path = @js_path)
			widgets.each { |widget| widget.save(path) }
		end

		def report
			widgets.each { |widget| widget.report }
		end

		def compile(path = File.dirname(@js_path))
			widgets.each { |widget| widget.compile(path) }
		end
	end

	# Utility methods
	private

	# removes styles that were added by default with the pre-mailer gem
	def self.remove_excess_premailer_content(html)
		html = html.gsub(/^.*?<body.*?>(.*?)<\/body>.*$/m,'\1') # removes created html and body tags
		html = html.gsub(/\s*font-size:\s*normal\s*;\s*/, '')
		html = html.gsub(/\s*font-style:\s*normal\s*;\s*/, '')
		html = html.gsub(/\s*font-variant:\s*normal\s*;\s*/, '')
		html = html.gsub(/\s*line-height:\s*normal\s*;\s*/, '')
		html = html.gsub(/\s*font-weight:\s*normal\s*;\s*/, '')
	end
end

